import { createHmmModel, Status, HmmModel } from "./hmmModel"

let mod: HmmModel

export interface HmmSegment {
    cut(str: string): string[]
}

export function createHmmMSegment(hmmSegDict: string): HmmSegment {
    if (!mod) {
        mod = createHmmModel(hmmSegDict)
    }
    return {
        cut
    }
}


export function cut(str: string): string[] {

    let left = 0
    let right = 0
    let end = str.length
    let res: string[] = []

    while (right !== end) {

        if (str.charCodeAt(right) < 0x80) {

            if (left != right) {
                internalCut(str.substring(left, right), res)

            }

            left = right

            do {

                right += sequentialLetterRule(str.substring(left)) // 连续 英文

                if (right !== left) {
                    break
                }

                right += numbersRule(str.substring(left)) // 连续数字

                if (right !== left) {
                    break
                }

                right++

            } while (false)

            let wr = str.substring(left, right)
            res.push(wr)
            left = right

        } else {
            right++

        }
    }

    if (left !== right) {
        internalCut(str.substring(left, right), res)

    }

    return res

}

function sequentialLetterRule(str: string): number {
    let i = 0
    while (str.length !== i) {

        const x = str.charCodeAt(i)
        if (97 <= x && x <= 122 || 65 <= x && x <= 90) {
            i++

        } else {
            return i

        }
    }

    return i
}

function numbersRule(str: string): number {
    let i = 0
    while (str.length !== i) {

        const x = str.charCodeAt(i)

        if (48 <= x && x <= 57 || 46 === x) {
            i++

        } else {
            return i

        }
    }
    return i
}

const MIN_DOUBLE = -3.14e+100

function internalCut(str: string, res: string[]) {

    let status: number[] = []

    viterbi(str, status)

    let left = 0
    let right = 0

    for (let i = 0; i < status.length; i++) {

        if (status[i] === Status.E || status[i] === Status.S) {

            right = i + 1
            let wr = str.substring(left, right)
            res.push(wr)
            left = right

        }
    }
}

function viterbi(str: string, status: number[]) {

    let Y = Status.Sum
    let X = str.length
    let XYSize = X * Y
    let now: number, old: number, stat: number, tmp: number, endE: number, endS: number
    let path: number[] = []
    let weight: number[] = []

    for (let y = 0; y < Y; y++) {

        weight[0 + y * X] = mod.startProb[y] + mod.getEmitProb(mod.emitProbVec[y], str.charCodeAt(0))
        path[0 + y * X] = -1

    }

    let emitProb: number

    for (let x = 1; x < X; x++) {

        for (let y = 0; y < Y; y++) {

            now = x + y * X
            weight[now] = MIN_DOUBLE
            path[now] = Status.E
            emitProb = mod.getEmitProb(mod.emitProbVec[y], str.charCodeAt(x))

            for (let preY = 0; preY < Y; preY++) {

                old = x - 1 + preY * X
                tmp = weight[old] + mod.transProb[preY][y] + emitProb

                if (tmp > weight[now]) {

                    weight[now] = tmp
                    path[now] = preY

                }

            }

        }

    }

    endE = weight[X - 1 + Status.E * X]
    endS = weight[X - 1 + Status.S * X]
    stat = 0

    if (endE >= endS) {
        stat = Status.E

    } else {
        stat = Status.S

    }

    for (let x = X - 1; x >= 0; x--) {

        status[x] = stat
        stat = path[x + stat * X]

    }

}